package componentes.mips;

import componentes.basicos.Barramento;
import componentes.basicos.Constantes;
import componentes.basicos.Palavra;
import componentes.basicos.Registrador;
import java.util.ArrayList;
import java.util.Random;

/**
 * Esta classe representa uma memória com um banco de registradores. Os 
 * registradores especificados nessa classe são os mesmos correspondentes
 * ao registradores MIPS.
 * 
 * @author Gustavo Miguel, Cleisson Alves, Evandro Francisco, Lucas.
 */


public class BancoDeRegistradores {
    
    /* Atributos da classe */
    private Registrador[] registradores;
    private ArrayList<String> stringsRegistradores;
    
    private Barramento entrada1;
    private Barramento entrada2;
    private Barramento saida1;
    private Barramento saida2;
    private Barramento barRegParaEscrita;
    private Barramento barDadosParaEscrita;
    private Barramento sinalEscreveReg;
    
    /**
     * Cria um banco de registradores, inicializa todos os registradores com conteudo nulo.
     */
    public BancoDeRegistradores() {
        barDadosParaEscrita = new Barramento();
        stringsRegistradores = new ArrayList<String>();
        setRegistradores();
        setBarramentos();
    }
   
    /**
     * Retorna o Barramento correspondente a saida do banco de registradores.
     * A primeira saida do banco de registradores contém a palavra do conteudo
     * do registrador especificado na primeira entrada.
     * @return Barramento da primeira saida do banco de registradores.
     */
    public Barramento getSaida1() {
        return saida1;
    }

    /**
     * Retorna o Barramento correspondente a saida do banco de registradores.
     * A segunda saida do banco de registradores contém a palavra do conteudo
     * do registrador especificado na segunda entrada.
     * @return Barramento da segunda saida do banco de registradores.
     */
    public Barramento getSaida2() {
        return saida2;
    }

    /**
     * Retorna o Barramento que corresponde ao sinal que indica se um dado
     * será escrito no registrador de destino.
     * @return Barramento que indica o sinal de escrita no registrador.
     */
    public Barramento getSinalEscreveReg() {
        return sinalEscreveReg;
    }

    /**
     * Configura o Barramento de sinal de escrita no registrador de destino.
     * @param sinalEscreveReg Barramento do sinal de escrita.
     */
    public void setSinalEscreveReg(Barramento sinalEscreveReg) {
        this.sinalEscreveReg = sinalEscreveReg;
    }

    /**
     * Retorna os registradores em formatos de Strings.
     * @return Lista de registradores com o formato de strings.
     */
    public ArrayList<String> getStringsRegistradores() {
        montaStringRegistradores();
        return stringsRegistradores;
    }
    
    /**
     * Método para escrita no Registrador de Destino. Uma escrita no registrador
     * de destino será efetuada se o sinal de controle de escrita for operante.
     */
    public void escreveRegDestino() {
        if(getSinalEscreveReg().getBitsValor() == 1) {
            for(int i = 0; i < registradores.length; ++i) {
                if(i == barRegParaEscrita.getBitsValor()) {
                    //System.out.println(barDadosParaEscrita.getBitsValor());
                    registradores[i].setConteudo(barDadosParaEscrita.getPalavra());
                }
            }
        }
    }

    /**
     * Configura o Barramento de escrita de dados.
     * @param barDadosParaEscrita Barramento de escrita de dados.
     */
    public void setBarDadosParaEscrita(Barramento barDadosParaEscrita) {
        this.barDadosParaEscrita.setBits(barDadosParaEscrita.getPalavra());
    }

    /**
     * Configura o Barramento de sinal de escrita no Registrador.
     * @param barRegParaEscrita Barramento de sinal de escrita.
     */
    public void setBarRegParaEscrita(Barramento barRegParaEscrita) {
        this.barRegParaEscrita = barRegParaEscrita;
    }

    /**
     * Configura o Barramento da primeira entrada do banco de registradores. 
     * A entrada especifica qual registrador no banco de registradores será 
     * acessado.
     * @param entrada1 da primeira entrada do Bando de Registradores.
     */
    public void setEntrada1(Barramento entrada1) {
        this.entrada1 = entrada1;
    }

     /**
     * Configura o Barramento da segunda entrada do banco de registradores. 
     * A entrada especifica qual registrador no banco de registradores será 
     * acessado.
     * @param entrada2 Barramento da segunda entrada do Bando de Registradores.
     */
    public void setEntrada2(Barramento entrada2) {
        this.entrada2 = entrada2; 
    }

    /**
     * Configura o Barramento da primeira saida do Banco de Registradores. A saida do 
     * banco de registradores é configurada com o conteúdo do registrador
     * indicado na entrada.
     */
    public void setSaida1() {
        this.saida1 = new Barramento(getConteudo(entrada1.getBitsValor()));
    }

    public void setSaida1(Barramento saida) {
        this.saida1 = saida;
    }

    public void setSaida2(Barramento saida) {
        this.saida2 = saida;
    }

    /**
     * Configura o Barramento da segunda saida do Banco de Registradores. A saida do 
     * banco de registradores é configurada com o conteúdo do registrador
     * indicado na entrada.
     */    
    public void setSaida2() {
        this.saida2 = new Barramento(getConteudo(entrada2.getBitsValor()));
    }
    

    /**
     * Método para retorna a Palavra contida em dos Registradores de acordo 
     * com o número do registrador.
     * @param registrador Número do registrador
     * @return Palavra que é o conteúdo do registrador
     * @see Palavra
     */
    public Palavra getConteudo(int registrador) {
        for(int i = 0; i < 32; ++i) {
            if(registrador == i) {
                return this.registradores[i].getConteudo();
            }
        }
        return null;
    }
    
    
    /**
     * Método para montar a String de saida do registrador.
     */
    public void montaStringRegistradores() {
        stringsRegistradores.clear();
        stringsRegistradores.add("Registradores");
        for(int i = 0; i < registradores.length; ++i) {
            stringsRegistradores.add(Instrucao.getRegistradores().get(i)[0] +" = "+registradores[i].getConteudoValorDec());
        }
    }
    
    /* Metodos privados */
    // Configura um registrador com conteudos aleatorios
    private void setRegistradores() {
        
        this.registradores = new Registrador[32];
        this.registradores[0] = new Registrador(Constantes.INOPERANTE);
        for(int i = 1; i < 32; ++i) {
            registradores[i] = new Registrador();
        }
        this.registradores[18] = new Registrador(new Palavra("00000000000000000000000000000001"));
    }
    
    // Método para configurar os barramentos dos registradores.
    private void setBarramentos() {
        this.barDadosParaEscrita = new Barramento(new Palavra());
        this.barRegParaEscrita = new Barramento(new Palavra());
        this.entrada1 = new Barramento(new Palavra());
        this.entrada2 = new Barramento(new Palavra());
        this.saida1 = new Barramento(new Palavra());
        this.saida2 = new Barramento(new Palavra());
    }
} // Fim da classe.
